﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Security.Cryptography;
using RandomNumberGeneration;

namespace JQuery.WebKit.Common.Utilities
{
    /// <summary>
    /// Generates random things using MersenneTwister algorithm.
    /// For other random number generator algorithms, please see http://www.c-sharpcorner.com/UploadFile/rmcochran/random07172006175425PM/random.aspx
    /// </summary>
    public static class Rnd
    {
        private static ReadOnlyCollection<byte> _safeUrlCharCodes = new ReadOnlyCollection<byte>(new byte[] { 33, 39, 40, 41, 42, 45, 46, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 95, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122 });
        private static ReadOnlyCollection<byte> _uCaseCharCodes = new ReadOnlyCollection<byte>(new byte[] { 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90 });
        private static ReadOnlyCollection<byte> _lCaseCharCodes = new ReadOnlyCollection<byte>(new byte[] { 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122 });
        private static ReadOnlyCollection<byte> _digitCharCodes = new ReadOnlyCollection<byte>(new byte[] { 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 });
        private static ReadOnlyCollection<byte> _asciiCharCodes = new ReadOnlyCollection<byte>(new byte[] { 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126 });
        private static ReadOnlyCollection<byte> _alphaNumericCharCodes = new ReadOnlyCollection<byte>(new byte[] { 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122 });

        private static RandomBase _random = new MersenneTwister();

        public static int GetRandom(int maxValue)
        {
            return _random.Next(maxValue);
        }

        public static double GetRandomDouble()
        {
            return _random.NextDouble();
        }

        public static double GetRandom(double maxValue)
        {
            return _random.NextDouble(maxValue);
        }

        public static byte[] GetRandomBytes(int length)
        {
            byte[] randomBuffer = new byte[length];
            _random.NextBytes(randomBuffer);
            return randomBuffer;
        }

        public static byte[] GetRandomBytes(int length, bool nonZero)
        {
            byte[] randomBuffer = new byte[length];
            using (RNGCryptoServiceProvider cryptoProvider = new RNGCryptoServiceProvider())
            {
                if (nonZero)
                {
                    cryptoProvider.GetBytes(randomBuffer);
                }
                else
                {
                    cryptoProvider.GetNonZeroBytes(randomBuffer);
                }
            }
            return randomBuffer;
        }

        public static string GetRandomString(int length)
        {
            byte[] randomBuffer = new byte[length];
            _random.NextBytes(randomBuffer);
            string s = string.Empty;
            Array.ForEach(randomBuffer, item => s += (char)item);
            return s;
        }

        public static string GetRandomPathString(int length)
        {
            return length > 1 ? string.Format("~{0}", GetRandomAlphaNumericString(length - 1)) : GetRandomAlphaNumericString(length);
        }

        public static string GetRandomUrlString(int length)
        {
            return string.Concat(GetRandomItems(_safeUrlCharCodes, length));
        }

        public static string GetRandomAlphaNumericString(int length)
        {
            return string.Concat(new System.Text.UTF8Encoding(false, false).GetString(GetRandomItems(_alphaNumericCharCodes, length).ToArray()));
        }

        public static string GetRandomASCIIString(int length)
        {
            return string.Concat(new System.Text.ASCIIEncoding().GetString(GetRandomItems(_asciiCharCodes, length).ToArray()));
        }

        private static List<byte> GetRandomItems(IList<byte> list, int length)
        {
            var selected = new List<byte>();
            var needed = length;
            var available = list.Count;
            var rand = new Random();
            while (selected.Count < length)
            {
                if (GetRandomDouble() < (double)needed / available)
                {
                    selected.Add(list[available - 1]);
                    needed--;
                }
                available--;
            }
            return selected;
        }
    }
}
